from contextlib import asynccontextmanager
from pathlib import Path

from fastapi import FastAPI, Request, HTTPException, status
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles

from rosa.scarborough_fair.custom_fastapi import openapi_url, docs_url, redoc_url, custom_openapi, get_custom_contact, get_custom_license_info
from rosa.scarborough_fair.generator_setting.settings import settings, logger
from rosa.scarborough_fair.global_router import routers, tags_metadata
from rosa.scarborough_fair.middleware.middlewares import middlewares
from rosa.scarborough_fair.status.types.exception import SiteException
from rosa.scarborough_fair.status.types.response import Status

try:
    from apps.on_event import on_startup
except ImportError as e:
    if hasattr(settings, "ROSA_TEST") and settings.ROSA_TEST:
        from rosa.rosa_apps.on_event import on_startup
    else:
        on_startup = None
        logger.error(f"导入on_startup失败，失败原因为 {e}")

try:
    from apps.on_event import on_shutdown
except ImportError as e:
    if hasattr(settings, "ROSA_TEST") and settings.ROSA_TEST:
        from rosa.rosa_apps.on_event import on_shutdown
    else:
        on_shutdown = None
        logger.error(f"导入on_shutdown失败，失败原因为 {e}")


# ####################
# ###  项目启停处理  ###
# ####################
@asynccontextmanager
async def lifespan(app: FastAPI):
    # startup event
    try:
        if on_startup:
            await on_startup()
    except Exception as e:
        logger.error(f"项目启动失败，错误原因为 {e}")
        raise e
    logger.info("项目启动成功")

    yield

    # shutdown event
    try:
        if on_shutdown:
            await on_shutdown()
    except Exception as e:
        logger.error(f"项目关闭失败，错误原因为 {e}")
        raise e
    logger.info("项目关闭成功")


app = FastAPI(
    title=settings.SITE_TITLE,
    description=settings.SITE_DESCRIPTION,
    version=settings.DOCS_VERSION,
    terms_of_service=settings.TERMS_OF_SERVICE,
    contact=get_custom_contact(),
    license_info=get_custom_license_info(),
    openapi_url=openapi_url,
    docs_url=docs_url,
    redoc_url=redoc_url,
    lifespan=lifespan
)

# 导入全局中间件
for middleware in middlewares:
    app.add_middleware(**middleware)

# 导入路由信息
for router in routers:
    app.include_router(**router, prefix=settings.URL_PREFIX)

app.openapi_tags = tags_metadata
app.openapi = custom_openapi(app)

# 自定义静态文件路径
if settings.STATIC_URL and settings.STATIC_PATH:
    # 获取当前路径
    static_path = Path.cwd()
    # 判断结尾是否是program，如果是则取消和settings.STATIC_PATH的拼接
    if static_path.name == "program":
        static_path = static_path.parent / settings.STATIC_PATH
    else:
        static_path = static_path / settings.STATIC_PATH

    if not static_path.exists():
        static_path.mkdir(parents=True)
    app.mount(settings.STATIC_URL, StaticFiles(directory=static_path), name="static")


# ####################
# ###   异常处理    ###
# ####################

# 主动抛出异常
@app.exception_handler(SiteException)
async def unicorn_site_exception_handle(_: Request, exc: SiteException):
    return JSONResponse(
        status_code=exc.status_code,
        content=jsonable_encoder(exc.response),
        headers=exc.headers
    )


# 系统异常
@app.exception_handler(HTTPException)
async def unicorn_http_exception_handle(_: Request, exc: HTTPException):
    if isinstance(exc.detail, Status):
        exception_status = exc.detail.code
        exception_message = exc.detail.message
    else:
        exception_status = str(exc.status_code)
        exception_message = exc.detail
    return JSONResponse(
        status_code=exc.status_code,
        content=jsonable_encoder({
            "code": exception_status,
            "message": exception_message,
            "success": False,
            "data": dict(),
        }),
        headers=exc.headers
    )


# 数据解析异常
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(_: Request, exc: RequestValidationError):
    errors = exc.errors()
    logger.error(f"数据解析出错，错误原因为 {errors}")

    data = dict()
    message = ''
    if errors and isinstance(errors, list):
        data = errors[0]
        field = len(data['loc']) > 1 and data['loc'][1] or data['loc'][0]
        msg = data['msg']
        message = f"字段 {field} 不合法 => {msg}"
    return JSONResponse(
        status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
        content=jsonable_encoder({
            "code": status.HTTP_422_UNPROCESSABLE_ENTITY,
            "message": message,
            "success": False,
            "data": data,
        })
    )
